package dk.itu.mobile.server;

import java.io.*;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.LinkedList;
import java.util.Queue;

import dk.itu.mobile.util.TaskListSerializer;
import dk.itu.mobile.vo.QueueItem;
import dk.itu.mobile.vo.Status;
import dk.itu.mobile.vo.Task;
import dk.itu.mobile.vo.TaskList;


/**
 * @author Group-02
 * 
 * TaskServer mantains a tasklist and awaits request from Clients using UDP protocol
 * When a request is received, it is put in a Queue (TaskQueue) for processing.
 * A TaskQueue (inner class) tries to process all incoming requests and if not succesfull
 * they are dequeued again for later processing.
 * If a reqest can not be processed because of a deadlock, it will be aborted after 10 unsuccesfull tries.
 * 
 *  
 */
/**
 * @author jacobbj
 *
 */
public class TaskServer {
	protected DatagramSocket socket = null;
	protected BufferedReader in = null;
	protected boolean moreRequests = true;
	private TaskList taskList;
	private TaskQueue taskQueue;


	// Initiates a server and a TaskQueue in seperate threads.
	public static void main(String[] args) throws IOException {
		TaskServer server = new TaskServer();
		server.taskQueue = server.new TaskQueue();
		server.taskQueue.socket = server.socket;
		Thread queueThread = new Thread(server.taskQueue);
		queueThread.start();
		server.start();
	}

	public TaskServer() throws IOException {
		this("TaskServerThread");
	}

	public TaskServer(String name) throws IOException {
		socket = new DatagramSocket(4445);
		taskList = TaskListSerializer.load("data/task-manager-revised.xml",
				TaskList.class);
	}

	public void start() {
		System.out.println("Starting Server");

		// Keeps waiting for incoming requests from Clients
		while (moreRequests) {
			try {
				byte[] buf = new byte[256];

				// receive request
				DatagramPacket packet = new DatagramPacket(buf, buf.length);
				socket.receive(packet);
				String request = new String(packet.getData());
				System.out.println("Incoming Client request queued: " + request);

				// get address and port from Client
				InetAddress address = packet.getAddress();
				int port = packet.getPort();

				//create queueItem and add to queue
				QueueItem q = new QueueItem(request, address, port);
				taskQueue.addItem(q);

			} catch (IOException e) {
				e.printStackTrace();
				moreRequests = false;
			}
		}
		socket.close();
	}

	/**
	 * A TaskQueue, maintains queue of requests and process all incoming request and if not succesfull
	 * a request is dequeued again for later processing.
	 * If a request can not be processed because of a deadlock, it will be aborted after a number of unsuccesfull tries.
	 *
	 */


	private class TaskQueue implements Runnable {
		private Queue<QueueItem> queue;
		public DatagramSocket socket;
		private int maxloop = 10;

		@Override
		public void run() {

			queue = new LinkedList<QueueItem>();

			initiateRunQueueIteration();

		}

		public void addItem(QueueItem item) {
			queue.offer(item);
		}

		public void initiateRunQueueIteration() {

			while (true) {
				if(queue.peek() != null) { 

					// try to process Item;

					QueueItem Qi = queue.poll();

					try {

						byte[] buf = new byte[256];
						DatagramPacket packet = new DatagramPacket(buf, buf.length);
						InetAddress address = Qi.getIAddress();
						int port = Qi.getPort();

						// Try to process the requestItem
						String result = process(Qi);
						System.out.println(result);
						buf = result.getBytes();

						// send the response to the client at "address" and "port"
						packet = new DatagramPacket(buf, buf.length, address, port);
						socket.send(packet);

					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}


				} else {

					try {
						System.out.println("Nothing in the queue, waiting 5 seconds");
						Thread.sleep(2500);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			} 
		}

		private synchronized String process(QueueItem item) {
			System.out.println("Proccessing");

			String msg = "";
			String r = item.getRequest();

			// Check validity of request
			if (taskList.getTask(r) == null)
				msg += "No such task exists ";
			else {
				Task t = taskList.getTask(r);

				// check if conditions has been executed or are required
				if (!t.conditions.equals("")) {
					String[] conditions = t.conditions.split(",");
					Task cTask;
					// check all conditions
					for (String c : conditions) {
						cTask = taskList.getTask(c);
						// return error if not executed
						if (cTask.status == Status.NOT_EXECUTED)
							msg += "Task: " + c + " must be executed first \n";
						// return error if condition task is required by other
						if (cTask.required)
							msg += "Task: " + c + " is required by other task \n";
					}
				}

				if (msg != "") {


					if (item.tries++ > maxloop ) {
						msg += "Task has been aborted due to " + maxloop + " unsuccesfull tries.. ";
					}

					// Put Item back in queue
					else {
						taskQueue.addItem(item);
						msg += "Task is re-queued and will be processed later..";
					}

					return msg;

				}

				else {
					// Task ready for processing
					t.status = Status.EXECUTED;
					t.required = false;
					// set response tasks as required
					if (!t.responses.equals("")) {
						String[] responses = t.responses.split(",");
						Task rTask;
						for (String responser : responses) {
							rTask = taskList.getTask(responser);
							rTask.required = true;
						}
					}

					msg = "request: " + t.id.toString() + " will be processed";
				}
			}
			// return message to client
			return msg;

		}

	}

}


